﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;

namespace BAASearchLib
{
    public delegate void SearchingFolderEvent(string path);

    public class BAASearch
    {
        private string location;
        private string fileName;
        private string author;
        private DateType dateType;
        private DateTime date;
        private DatePeriod datePeriod;
        private string fileType;
        private string contents;
        private IList<ResultItem> results;
        private IList<FileComparer> fileComparers;

        public event SearchingFolderEvent searchingFolderEvent;

        /// <summary>
        /// Construct a search from the specified location for the specified attributes.
        /// Require: The string attributes are trimed (no spaces before and after)
        ///  /// </summary>
        /// <param name="location">The location to start from</param>
        /// <param name="fileName">The string to search for in a filename</param>
        /// <param name="author"></param>
        /// <param name="dateType"></param>
        /// <param name="date"></param>
        /// <param name="datePeriod"></param>
        /// <param name="fileType"></param>
        /// <param name="contents"></param>
        /// <param name="results"></param>
        public BAASearch(string location, string fileName, string author, DateType dateType, DateTime date, DatePeriod datePeriod, string fileType, string contents, IList<ResultItem> results)
        {
            this.location = location;
            this.fileName = fileName;
            this.author = author;
            this.dateType = dateType;
            this.date = date;
            this.datePeriod = datePeriod;
            this.fileType = fileType;
            this.contents = contents;
            this.results = results;

            fileComparers = new List<FileComparer>();

            // look at all the parameters and decide which file comparers to use.
            if (location == null || location.Length == 0)
                throw new ArgumentNullException("location", "Location is missing, can't search");
            if (fileName != null && fileName.Length > 0)
                fileComparers.Add(new CompareFileName(fileName));
            if (author != null && author.Length > 0)
                fileComparers.Add(new CompareAuthor(author));
            // --------
            if (dateType == DateType.Created)
                fileComparers.Add(new CompareDateCreated(date, datePeriod));
            else if (dateType == DateType.Modified)
                fileComparers.Add(new CompareDateModified(date, datePeriod));
            // --------
            if (fileType != null && fileType.Length > 0)
                fileComparers.Add(new CompareFileType(fileType));
            if (contents != null && contents.Length > 0)
                fileComparers.Add(new CompareContents(contents));
            if (results == null)
                throw new ArgumentNullException("results", "Don't have a reference to results location, can't search");
        }


        /// <summary>
        /// Construct a sub-search using the same attributes.
        /// </summary>
        /// <param name="location">The location to start from</param>
        /// <param name="fileName">The string to search for in a filename</param>
        /// <param name="author"></param>
        /// <param name="dateType"></param>
        /// <param name="date"></param>
        /// <param name="datePeriod"></param>
        /// <param name="fileType"></param>
        /// <param name="contents"></param>
        /// <param name="results"></param>
        /// <param name="fileComparers"></param>
        private BAASearch(string location, string fileName, string author, DateType dateType, DateTime date, DatePeriod datePeriod, string fileType, string contents, IList<ResultItem> results, List<FileComparer> fileComparers)
            : this(location, fileName, author, dateType, date, datePeriod, fileType, contents, results)
        {
            this.fileComparers = fileComparers;
        }

        /// <summary>
        /// Start the search
        /// </summary>
        public void Search()
        {
            /*
             * get the contents of location-folder
             * for each file
             *   check using the filecomparers
             *      if all true
             *          create resultitem and add to results
             * for each folder
             *   start a new search (new thread)
             * wait for all child-threads
             */

            searchingFolderEvent(location);

            string searchPattern = "*"; // Default search filter

            // Filter the files list, for efficiency.
            foreach (FileComparer fileComparer in fileComparers)
            {
                if (fileComparer is CompareFileName)
                    searchPattern += fileName + "*";
                if (fileComparer is CompareFileType)
                    searchPattern += "." + fileType;
            }

            string[] files = Directory.GetFiles(location, searchPattern);
            int fileMatch; // the number of matches

            foreach (string file in files)
            {
                fileMatch = 0;
                FileInfo fileInfo = new FileInfo(file);

                // check files for match
                foreach (FileComparer fileComparer in fileComparers)
                    // if we get a match, then increment by one.
                    if (fileComparer.Compare(fileInfo))
                        fileMatch++;

                /* if the number of matches equals to the number of comparers, 
                 * we have a full match and we add it to the list
                 */
                if (fileMatch == fileComparers.Count)
                {
                    results.Add(new ResultFile(fileInfo));
                }
            }

            string[] directories = Directory.GetDirectories(location);

            // Start a new search within each directory
            foreach (string directory in directories)
            {
                BAASearch baaSearch = new BAASearch(directory, fileName, author, dateType, date, datePeriod, fileType, contents, results);
                baaSearch.searchingFolderEvent = searchingFolderEvent;
                try
                {
                    baaSearch.Search();
                }
                catch (UnauthorizedAccessException)
                {
                    // Do nothing, skip this folder.
                }

            }
        }

        /// <summary>
        /// Description: Realising/Disposing objects which are not in use any more
        /// -----------------------------------------------------
        /// From Vytas:
        /// -usually you want to call it when you don;t need your object anymore... 
        /// but it is working best with COM objects, like Word, Excel, 
        /// Powerpoint and other references
        /// -you can call it on Word document, and word APP
        /// -but befor you call it you still have to write wordApp.Quit();
        /// -----------------------------------------------------
        /// Parameter: object obj
        /// </summary>
        /// <param name="obj"></param>
        public void releaseObject(object obj)
        {
            try
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                obj = null;
            }
            catch (Exception ex)
            {
                obj = null;
                MessageBox.Show("Unable to release the Object " + ex.ToString());
            }
            finally
            {
                GC.Collect();
            }
        }
    }
}
